//
// Created by andy on 2018/3/10.
//
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <execinfo.h>
#include "logger.h"
using namespace yfxt;

Logger* Logger::sInstance = NULL;

static const char * gLogFormat[] = {
        "INFO[%s]:%s\n",
        "DEBUG[%s]:%s\n",
        "WARNING[%s]:%s\n",
        "ERROR[%s]:%s\n",
        "TRACE[%s]:%s\n"
};

Logger* Logger::instance() {
    if (NULL == sInstance)
        sInstance = new Logger;
    return sInstance;
}

Logger::Logger():mCache(NULL),mOutputFile(NULL),mCacheOffset(0), mHasInitialized(false) {

}

Logger::~Logger() {
    close();
}

void Logger::initialize(const char *path, int traceDepth, bool serialize) {
    mCache = new char[MAX_CACHE_SIZE];
    memset(mCache, 0, MAX_CACHE_SIZE);

    DIR * dir = opendir(path);
    if (NULL == dir)
       return;
    mFileName = path;
    time_t timestamp;
    time(&timestamp);
    struct tm * timeinfo;
    timeinfo = localtime(&timestamp);
    if (mFileName.at(mFileName.size() - 1) == '/')
        mFileName.append("Log_");
    else
        mFileName.append("/Log_");
    char szTimeInfo[32] = {0};
    strftime(szTimeInfo, 32, "%Y%m%d", timeinfo);
    mFileName.append(szTimeInfo);
    mOutputFile = fopen(mFileName.c_str(), "a+");
    if (NULL == mOutputFile)
        return;
    fseek(mOutputFile, 0, SEEK_END);
    mIsSerialize = serialize;
    mHasInitialized = true;
    mTraceDepth = traceDepth < MAX_TRACE_DEPTH ? traceDepth : MAX_TRACE_DEPTH;
    pthread_mutex_init(&mMutex, NULL);
}

void Logger::close() {
    if (NULL != mCache) {
        delete []mCache;
        mCache = NULL;
    }
    if (NULL != mOutputFile){
        fclose(mOutputFile);
        mOutputFile = NULL;
    }

    mIsSerialize = false;
    mHasInitialized = false;
}

void Logger::log(LogLevel level, const char *msg) {
    if (!mHasInitialized)
        return;
    logformat(level, msg);
}

void Logger::log(LogLevel level, const char *file, const char *fun, int line,
                 const char *msg, bool trace) {
    if (!mIsSerialize)
        return;
    char head[256] = {0};
    sprintf(head, "file [%s] in function [%s] at line [%d]:", file, fun, line);
    std::string sMsg = head;
    sMsg.append(msg);
    sMsg.append(":\n");
    log(level, sMsg.c_str());
    if (trace)
    {
        void * buff[128];
        int nptrs = backtrace(buff, 128);
        char ** strings = backtrace_symbols(buff, nptrs);
        if (NULL == strings)
            return;
        int depth = mTraceDepth < nptrs ? mTraceDepth : nptrs;
        for (int i = 0; i < depth; ++i)
        {
            std::string sTrace = "\t\t";
            sTrace.append(strings[i]);
            sTrace.append("\n");
            logout(sTrace.c_str());
        }
        free(strings);
    }
}

void Logger::logformat(LogLevel level, const char *msg) {
    if (NULL == msg)
        return;
    char szTimeInfo[32] = {0};
    time_t tm;
    time(&tm);
    struct tm * timeinfo = localtime(&tm);

    strftime(szTimeInfo, 80, "%Y%m%d_%H%M%S", timeinfo);
    struct timeval tv;
    gettimeofday(&tv, NULL);
    sprintf(szTimeInfo + 15, ".%.3lu", tv.tv_usec / 1000L);

    int fmno = int (level);
    const char * format = gLogFormat[fmno];
    char tempCache[MAX_CACHE_SIZE] = {0};
    snprintf(tempCache, MAX_CACHE_SIZE, format, szTimeInfo, msg);
    logout(tempCache);
}

void Logger::logout(const char * msg) {
    int len = strlen(msg);
    fwrite(mCache, sizeof(char), len, mOutputFile);
   	fflush(mOutputFile);
    
    /*if (!mIsSerialize)
    {

    } else
    {
        if ((mCacheOffset + len) < MAX_CACHE_SIZE)
        {
            memcpy(mCache, msg, len);
            mCacheOffset += len;
        } else{
            fwrite(mCache, sizeof(char), mCacheOffset, mOutputFile);
            fflush(mOutputFile);
            memset(mCache, 0, MAX_CACHE_SIZE);
            mCacheOffset = 0;
            memcpy(mCache, msg, len);
            mCacheOffset += len;
        }
    }*/

}
